// 实现语音识别子服务
#pragma once
#include <brpc/server.h>
#include <butil/logging.h>

#include "etcd.hpp"     // 服务注册模块
#include "logger.hpp"   // 日志模块
#include "utils.hpp"    // 工具模块
#include "file.pb.h"    // protobuf框架代码
#include "base.pb.h"    // protobuf框架代码

namespace im
{
// 继承FileService类，重写rpc业务处理虚函数
class FileServiceImpl  : public im::FileService
{
private:
    std::string _storage_path; // 文件存储路径
public:
    FileServiceImpl(const std::string& storage_path)
        : _storage_path(storage_path)
    {
        umask(0);
        mkdir(storage_path.c_str(), 0775);
        if (_storage_path.back() != '/') _storage_path.push_back('/');
    }
    virtual void GetSingleFile(google::protobuf::RpcController* controller,
                                const ::im::GetSingleFileReq* request,
                                ::im::GetSingleFileRsp* response,
                                ::google::protobuf::Closure* done)
    {
        // ClosureGuard功能同智能指针，防止用户忘记调用run函数，超出作用于自动调用
        brpc::ClosureGuard rpc_guard(done);
        // 1. 统一设置返回的request_id
        response->set_request_id(request->request_id());
        // 2. 获取并组织文件名，16位uuid
        std::string fid = request->file_id();
        std::string filename = _storage_path + fid;
        // 3. 读取文件
        std::string body;
        bool ret = readFile(filename, body);
        if (ret == false) {
            // 组织错误响应
            response->set_success(false);
            response->set_errmsg("读取文件数据失败！");
            LOG_ERROR("{}读取文件数据失败！", request->request_id());
            return;
        }
        // 4. 组织正确响应，这里protobuf提供了mutable_file_data接口，可以修改file_data内容
        // 如果不添加mutable则不能进行修改，自带const属性
        response->set_success(true);
        response->mutable_file_data()->set_file_id(fid);
        response->mutable_file_data()->set_file_content(body);
    }
    virtual void GetMultiFile(google::protobuf::RpcController* controller,
                                const ::im::GetMultiFileReq* request,
                                ::im::GetMultiFileRsp* response,
                                ::google::protobuf::Closure* done)
    {
        // ClosureGuard功能同智能指针，防止用户忘记调用run函数，超出作用于自动调用
        brpc::ClosureGuard rpc_guard(done);
        // 1. 统一设置返回的request_id
        response->set_request_id(request->request_id());
        
        // 循环取出请求中的文件ID
        // protobuf提供了repeated类型的size接口，可以获取元素个数
        for (int i = 0; i < request->file_id_list_size(); i++)
        {
            std::string fid = request->file_id_list(i);
            std::string filename = _storage_path + fid;

            std::string body;
            bool ret = readFile(filename, body);
            if (ret == false) {
                // 组织错误响应
                response->set_success(false);
                response->set_errmsg("读取文件数据失败！");
                LOG_ERROR("{}读取文件数据失败！", request->request_id());
                return;
            }

            // response的file_data是一个hash_map，val类型是FileDownloadData
            FileDownloadData data;
            data.set_file_id(fid);
            data.set_file_content(body);
            response->mutable_file_data()->insert({fid, data});
        }
        response->set_success(true);
    }

    virtual void PutSingleFile(google::protobuf::RpcController* controller,
                                const ::im::PutSingleFileReq* request,
                                ::im::PutSingleFileRsp* response,
                                ::google::protobuf::Closure* done)
    {
        // ClosureGuard功能同智能指针，防止用户忘记调用run函数，超出作用于自动调用
        brpc::ClosureGuard rpc_guard(done);
        // 1. 统一设置返回的request_id
        response->set_request_id(request->request_id());

        // 生成uuid
        std::string fid = uuid();
        std::string filename = _storage_path + fid;
        // 写入文件
        bool ret = writeFile(filename, request->file_data().file_content());
        if (ret == false) {
            response->set_success(false);
            response->set_errmsg("写入文件数据失败！");
            LOG_ERROR("{}写入文件数据失败！", request->request_id());
            return;
        }
        // 组织响应
        response->set_success(true);
        response->mutable_file_info()->set_file_id(fid);
        response->mutable_file_info()->set_file_size(request->file_data().file_size());
        response->mutable_file_info()->set_file_name(request->file_data().file_name());
    }

    virtual void PutMultiFile(google::protobuf::RpcController* controller,
                                const ::im::PutMultiFileReq* request,
                                ::im::PutMultiFileRsp* response,
                                ::google::protobuf::Closure* done)
    {
        // ClosureGuard功能同智能指针，防止用户忘记调用run函数，超出作用于自动调用
        brpc::ClosureGuard rpc_guard(done);

        // 1. 统一设置返回的request_id
        response->set_request_id(request->request_id());
        for (int i = 0; i < request->file_data_size(); i++)
        {
            std::string fid = uuid();
            std::string filename = _storage_path + fid;

            bool ret = writeFile(filename, request->file_data(i).file_content());
            if (ret == false) {
                response->set_success(false);
                response->set_errmsg("写入文件数据失败！");
                LOG_ERROR("{}写入文件数据失败！", request->request_id());
                return;
            }
            im::FileMessageInfo* info = response->add_file_info();
            info->set_file_id(fid);
            info->set_file_size(request->file_data(i).file_size());
            info->set_file_name(request->file_data(i).file_name());
        }
        response->set_success(true);
    }
};

// 构建RPC服务器
class FileServer 
{
public:
    using ptr = std::shared_ptr<FileServer>;
private:
    Registry::ptr _reg_client;  // 封装的etcd服务注册类，需要三个参数
    std::shared_ptr<brpc::Server> _rpc_server; // brpc自己的Server类，需要三个参数
public:
    FileServer(const Registry::ptr& reg_client, const std::shared_ptr<brpc::Server>& server)
        : _reg_client(reg_client)
        , _rpc_server(server)
    {}

    //搭建RPC服务器，并启动服务器
    void start() 
    {
        _rpc_server->RunUntilAskedToQuit();
    }
};

// 采取建造者模式帮助创建FileServer对象
class FileServerBuilder 
{
private:
    Registry::ptr _reg_client;
    std::shared_ptr<brpc::Server> _rpc_server;
public:
    //用于构造服务注册客户端对象
    void make_reg_object(const std::string &reg_host, const std::string &service_name,
                        const std::string &access_host) 
    {
        _reg_client = std::make_shared<Registry>(reg_host);
        _reg_client->registry(service_name, access_host);
    }
    //构造RPC服务器对象
    void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads
                        , const std::string& path = "./data/") 
    {
        _rpc_server = std::make_shared<brpc::Server>();
        // 向服务器对象中新增speech_service服务
        // 需要注意speech_service对象要在最后才能销毁，所以需要申请在堆上
        FileServiceImpl* file_service = new FileServiceImpl(path);
        int ret = _rpc_server->AddService(file_service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
        if (ret == -1) {
            LOG_ERROR("添加Rpc服务失败！");
            abort();
        }
        // 启动brpc服务器
        brpc::ServerOptions options;
        options.idle_timeout_sec = timeout;
        options.num_threads = num_threads;
        ret = _rpc_server->Start(port, &options);
        if (ret == -1) {
            LOG_ERROR("服务启动失败！");
            abort();
        }
    }
    // 返回SpeechServer对象
    FileServer::ptr build() 
    {
        if (!_reg_client) {
            LOG_ERROR("还未初始化服务注册模块！");
            abort();
        }
        if (!_rpc_server) {
            LOG_ERROR("还未初始化RPC服务器模块！");
            abort();
        }

        FileServer::ptr server = std::make_shared<FileServer>(_reg_client, _rpc_server);
        return server;
    }
};
}